/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 screen_point.h

	$Header: /heroes4/screen_point.h $

	$NoKeywords: $

 ************************************************************************/

#if !defined( SCREEN_POINT_H_INCLUDED )
#define SCREEN_POINT_H_INCLUDED

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include <algorithm>
#include <cmath>
#include <list>

#include "math_constants.h"
#include "point_2d.h"
#include "streambuf_operators.h"

// ---------------------------------------------------------------
// data structure for screen coordinates
// ---------------------------------------------------------------
struct t_screen_point
{
	int x;
	int y;

	// Constructors
	t_screen_point();
	t_screen_point( int new_x, int new_y );
	t_screen_point( t_point_2d< int > const & other );

	// Operators
	t_screen_point & operator+=( t_screen_point const & right );
	t_screen_point & operator-=( t_screen_point const & right );
	t_screen_point & operator*=( int right );
	t_screen_point & operator/=( int right );
	t_screen_point & operator%=( int right );
	t_screen_point & operator<<=( int right );
	t_screen_point & operator>>=( int right );

	operator t_point_2d< int >() const;
};

// ---------------------------------------------------------------
// inline t_screen_point members
// ---------------------------------------------------------------
inline t_screen_point::t_screen_point() : x( 0 ), y( 0 )
{
}

inline t_screen_point::t_screen_point( int new_x, int new_y )
{
	x = new_x;
	y = new_y;
}

inline t_screen_point::t_screen_point( t_point_2d< int > const & other )
	:	x( other.x ),
		y( other.y )
{
}

inline t_screen_point & t_screen_point::operator+=( t_screen_point const & right )
{
	x += right.x;
	y += right.y;
	return *this;
}

inline t_screen_point & t_screen_point::operator-=( t_screen_point const & right )
{
	x -= right.x;
	y -= right.y;
	return *this;
}

inline t_screen_point & t_screen_point::operator*=( int right )
{
	x *= right;
	y *= right;
	return *this;
}

inline t_screen_point & t_screen_point::operator/=( int right )
{
	x /= right;
	y /= right;
	return *this;
}

inline t_screen_point & t_screen_point::operator%=( int right )
{
	x %= right;
	y %= right;
	return *this;
}

inline t_screen_point & t_screen_point::operator<<=( int right )
{
	x <<= right;
	y <<= right;
	return *this;
}

inline t_screen_point & t_screen_point::operator>>=( int right )
{
	x >>= right;
	y >>= right;
	return *this;
}

inline t_screen_point::operator t_point_2d< int >() const
{
	return t_point_2d< int >( x, y );
}

// ---------------------------------------------------------------
// inline t_screen_point free functions
// ---------------------------------------------------------------

inline bool operator==( t_screen_point const & left, t_screen_point const & right )
{
	return left.x == right.x && left.y == right.y;
}

inline bool operator!=( t_screen_point const & left, t_screen_point const & right )
{
	return !( left == right );
}

inline t_screen_point operator-( t_screen_point const & point )
{
	return t_screen_point( -point.x, -point.y );
}

inline t_screen_point operator+( t_screen_point const & left, t_screen_point const & right )
{
	t_screen_point result( left );
	result += right;
	return result;
}

inline t_screen_point operator-( t_screen_point const & left, t_screen_point const & right )
{
	t_screen_point result( left );
	result -= right;
	return result;
}

inline t_screen_point operator*( t_screen_point const & left, int right )
{
	t_screen_point result( left );
	result *= right;
	return result;
}

inline t_screen_point operator*( int left, t_screen_point const & right )
{
	return right * left;
}

inline t_screen_point operator/( t_screen_point const & left, int right )
{
	t_screen_point result( left );
	result /= right;
	return result;
}

inline t_screen_point operator%( t_screen_point const & left, int right )
{
	t_screen_point result( left );
	result %= right;
	return result;
}

inline t_screen_point operator<<( t_screen_point const & left, int right )
{
	t_screen_point result( left );
	result <<= right;
	return result;
}

inline t_screen_point operator>>( t_screen_point const & left, int right )
{
	t_screen_point result( left );
	result >>= right;
	return result;
}

inline double distance( t_screen_point const & point )
{
	return sqrt( (double)(point.x * point.x + point.y * point.y) );
}

inline double angle( t_screen_point const & point )
{
	double result = atan2( (double)point.y, (double)point.x );
	if ( result < 0.0 )
		result += k_pi * 2;
	return result;
}

inline std::streambuf & operator<<( std::streambuf & stream, t_screen_point const & point )
{
	put< t_int32 >( stream, point.x );
	put< t_int32 >( stream, point.y );
	return stream;
}

inline std::streambuf & operator>>( std::streambuf & stream, t_screen_point & point )
{
	t_screen_point result;
	result.x = get< t_int32 >( stream );
	result.y = get< t_int32 >( stream );
	point = result;
	return stream;
}

// ---------------------------------------------------------------
// data structure for screen rectangles
// ---------------------------------------------------------------
struct t_screen_rect
{
	int		left;
	int		top;
	int		right;
	int		bottom;

	// Constructors
	t_screen_rect();
	t_screen_rect( int left, int top, int right, int bottom );
	t_screen_rect( t_screen_point const & top_left, t_screen_point const & size );
	t_screen_rect( t_rect_2d< int > const & other );

	// Member functions
	int height() const;
	int width() const;

	t_screen_point top_left() const;
	t_screen_point top_right() const;
	t_screen_point bottom_left() const;
	t_screen_point bottom_right() const;

	t_screen_point size() const;

	bool           contains( t_screen_rect const& arg ) const; // true if rectangle entirely
                                                               // contains arg.
	// Operators
	t_screen_rect & operator+=( t_screen_point const & point );
	t_screen_rect & operator-=( t_screen_point const & point );
	t_screen_rect & operator*=( int arg );
	t_screen_rect & operator/=( int arg );
	t_screen_rect & operator<<=( int arg );
	t_screen_rect & operator>>=( int arg );

	operator t_rect_2d< int >() const;
};

// ---------------------------------------------------------------
// inline t_screen_rect members
// ---------------------------------------------------------------

inline t_screen_rect::t_screen_rect() : left( 0 ), top( 0 ), right( 0 ), bottom( 0 )
{
}

inline t_screen_rect::t_screen_rect( int new_left, int new_top, int new_right, int new_bottom )
	:	left( new_left ), top( new_top ), right( new_right ), bottom( new_bottom )
{
}

inline t_screen_rect::t_screen_rect( t_screen_point const & top_left, t_screen_point const & size )
	:	left( top_left.x ), top( top_left.y ), right( top_left.x + size.x ), bottom( top_left.y + size.y )
{
}

inline t_screen_rect::t_screen_rect( t_rect_2d< int > const & other )
	:	left( other.left ),
		top( other.top ),
		right( other.right ),
		bottom( other.bottom )
{
}

inline int t_screen_rect::height() const
{
	return bottom - top;
}

inline int t_screen_rect::width() const
{
	return right - left;
}

inline t_screen_point t_screen_rect::top_left() const
{
	return t_screen_point( left, top );
}

inline t_screen_point t_screen_rect::top_right() const
{
	return t_screen_point( right, top );
}

inline t_screen_point t_screen_rect::bottom_left() const
{
	return t_screen_point( left, bottom );
}

inline t_screen_point t_screen_rect::bottom_right() const
{
	return t_screen_point( right, bottom );
}

inline t_screen_point t_screen_rect::size() const
{
	return t_screen_point( width(), height() );
}

inline bool t_screen_rect::contains( t_screen_rect const& arg ) const
{
	return left <= arg.left && right >= arg.right && top <= arg.top && bottom >= arg.bottom;
}

inline t_screen_rect & t_screen_rect::operator+=( t_screen_point const & point )
{
	left += point.x;
	right += point.x;
	top += point.y;
	bottom += point.y;
	return *this;
}

inline t_screen_rect & t_screen_rect::operator-=( t_screen_point const & point )
{
	left -= point.x;
	right -= point.x;
	top -= point.y;
	bottom -= point.y;
	return *this;
}

inline t_screen_rect & t_screen_rect::operator*=( int arg )
{
	left *= arg;
	right *= arg;
	top *= arg;
	bottom *= arg;
	return *this;
}

inline t_screen_rect & t_screen_rect::operator/=( int arg )
{
	left /= arg;
	right /= arg;
	top /= arg;
	bottom /= arg;
	return *this;
}

inline t_screen_rect & t_screen_rect::operator<<=( int arg )
{
	left <<= arg;
	right <<= arg;
	top <<= arg;
	bottom <<= arg;
	return *this;
}

inline t_screen_rect & t_screen_rect::operator>>=( int arg )
{
	left >>= arg;
	right >>= arg;
	top >>= arg;
	bottom >>= arg;
	return *this;
}

inline t_screen_rect::operator t_rect_2d< int >() const
{
	return t_rect_2d< int >( left, top, right, bottom );
}

// ---------------------------------------------------------------
// inline t_screen_rect free functions
// ---------------------------------------------------------------

inline bool operator==( t_screen_rect const & left, t_screen_rect const & right )
{
	return left.left == right.left && left.top == right.top && left.right == right.right && left.bottom == right.bottom;
}

inline bool operator!=( t_screen_rect const & left, t_screen_rect const & right )
{
	return !( left == right );
}

inline t_screen_rect operator+( t_screen_rect const & left, t_screen_point const & right )
{
	t_screen_rect result( left );
	result += right;
	return result;
}

inline t_screen_rect operator+( t_screen_point const & left, t_screen_rect const & right )
{
	return right + left;
}

inline t_screen_rect operator-( t_screen_rect const & left, t_screen_point const & right )
{
	t_screen_rect result( left );
	result -= right;
	return result;
}

inline t_screen_rect operator*( t_screen_rect const & left, int right )
{
	t_screen_rect result( left );
	result *= right;
	return result;
}

inline t_screen_rect operator*( int left, t_screen_rect const & right )
{
	return right * left;
}

inline t_screen_rect operator/( t_screen_rect const & left, int right )
{
	t_screen_rect result( left );
	result /= right;
	return result;
}

inline t_screen_rect operator<<( t_screen_rect const & left, int right )
{
	t_screen_rect result( left );
	result <<= right;
	return result;
}

inline t_screen_rect operator>>( t_screen_rect const & left, int right )
{
	t_screen_rect result( left );
	result >>= right;
	return result;
}

inline bool is_point_in_rect( t_screen_point const & point, t_screen_rect const & rect )
{
	return point.x >= rect.left && point.x < rect.right && point.y >= rect.top && point.y < rect.bottom;
}

inline bool is_normalized( t_screen_rect const & rect )
{
	return rect.left <= rect.right && rect.top <= rect.bottom;
}

inline void normalize( t_screen_rect & rect )
{
	if ( rect.left > rect.right )
		std::swap( rect.left, rect.right );

	if ( rect.top > rect.bottom )
		std::swap( rect.top, rect.bottom );
}

inline bool intersect( t_screen_rect const & left, t_screen_rect const & right )
{
	return		left.left < right.right
			&&	right.left < left.right
			&&	left.top < right.bottom
			&&	right.top < left.bottom;
}

inline t_screen_rect intersection( t_screen_rect const & left, t_screen_rect const & right )
{
	return	t_screen_rect(
				left.left > right.left ? left.left : right.left,
				left.top > right.top ? left.top : right.top,
				left.right < right.right ? left.right : right.right,
				left.bottom < right.bottom ? left.bottom : right.bottom );
}

// return the rectangle enclosing both rectangles.
inline t_screen_rect get_extent( t_screen_rect const& left, t_screen_rect const& right )
{
	return t_screen_rect( left.left   < right.left   ? left.left : right.left,
		                  left.top    < right.top    ? left.top  : right.top,
						  left.right  > right.right  ? left.right : right.right,
						  left.bottom > right.bottom ? left.bottom : right.bottom );
}

inline std::streambuf & operator<<( std::streambuf & stream, t_screen_rect const & rect )
{
	put< t_int32 >( stream, rect.left );
	put< t_int32 >( stream, rect.top );
	put< t_int32 >( stream, rect.right );
	put< t_int32 >( stream, rect.bottom );
	return stream;
}

inline std::streambuf & operator>>( std::streambuf & stream, t_screen_rect & rect )
{
	t_screen_rect result;
	result.left = get< t_int32 >( stream );
	result.top = get< t_int32 >( stream );
	result.right = get< t_int32 >( stream );
	result.bottom = get< t_int32 >( stream );
	rect = result;
	return stream;
}

typedef std::list<t_screen_rect>				t_screen_rect_list;

#endif // !defined( SCREEN_POINT_H_INCLUDED )
